using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using UnityEngine;

namespace AdvancedInspector
{
	[Serializable]
	[AdvancedInspector]
	public class ActionBinding : ICopiable
	{
		[Serializable]
		[AdvancedInspector]
		public class BindingParameter : ICopiable, ICopy
		{
			public enum BindingType
			{
				Internal = 0,
				Static = 1,
				External = 2
			}

			private enum BindingValueType
			{
				None = 0,
				Boolean = 1,
				Integer = 2,
				Float = 3,
				String = 4,
				Vector2 = 5,
				Vector3 = 6,
				Vector4 = 7,
				Color = 8,
				Rect = 9,
				Bounds = 10,
				Reference = 11
			}

			[Restrict("RestrictBinding")]
			[Inspect(-1)]
			public BindingType binding;

			private bool canBeInternal = true;

			[SerializeField]
			private BindingValueType type;

			[SerializeField]
			private string qualifiedTypeName;

			[SerializeField]
			private bool boolValue;

			[SerializeField]
			private int intValue;

			[SerializeField]
			private float floatValue;

			[SerializeField]
			private string stringValue = string.Empty;

			[SerializeField]
			private Vector2 vector2Value = Vector2.zero;

			[SerializeField]
			private Vector3 vector3Value = Vector3.zero;

			[SerializeField]
			private Vector4 vector4Value = Vector4.zero;

			[SerializeField]
			private Color colorValue = Color.black;

			[SerializeField]
			private Rect rectValue = new Rect(0f, 0f, 0f, 0f);

			[SerializeField]
			private Bounds boundsValue = new Bounds(Vector3.zero, Vector3.zero);

			[SerializeField]
			private UnityEngine.Object referenceValue = new UnityEngine.Object();

			[SerializeField]
			private GameObject gameObject;

			[SerializeField]
			private UnityEngine.Component component;

			[SerializeField]
			private string method;

			private bool CanBeInternal
			{
				get
				{
					return canBeInternal;
				}
			}

			public Type Type
			{
				get
				{
					if (string.IsNullOrEmpty(qualifiedTypeName))
					{
						return null;
					}
					return Type.GetType(qualifiedTypeName);
				}
				set
				{
					if (value == typeof(bool))
					{
						type = BindingValueType.Boolean;
					}
					else if (value == typeof(Bounds))
					{
						type = BindingValueType.Bounds;
					}
					else if (value == typeof(Color))
					{
						type = BindingValueType.Color;
					}
					else if (value == typeof(float))
					{
						type = BindingValueType.Float;
					}
					else if (value == typeof(int))
					{
						type = BindingValueType.Integer;
					}
					else if (value == typeof(Rect))
					{
						type = BindingValueType.Rect;
					}
					else if (typeof(UnityEngine.Object).IsAssignableFrom(value))
					{
						type = BindingValueType.Reference;
					}
					else if (value == typeof(string))
					{
						type = BindingValueType.String;
					}
					else if (value == typeof(Vector2))
					{
						type = BindingValueType.Vector2;
					}
					else if (value == typeof(Vector3))
					{
						type = BindingValueType.Vector3;
					}
					else if (value == typeof(Vector4))
					{
						type = BindingValueType.Vector4;
					}
					else
					{
						type = BindingValueType.None;
					}
					if (type != 0)
					{
						qualifiedTypeName = value.AssemblyQualifiedName;
					}
					else
					{
						qualifiedTypeName = string.Empty;
					}
				}
			}

			[Inspect(-2)]
			public string BoundType
			{
				get
				{
					if (Type == null)
					{
						return string.Empty;
					}
					return Type.Name;
				}
			}

			[Inspect("IsStatic")]
			[RuntimeResolve("GetRuntimeType")]
			public object Value
			{
				get
				{
					if (binding == BindingType.External)
					{
						object obj = Invoke();
						if (obj.GetType().IsAssignableFrom(Type))
						{
							return obj;
						}
						TypeConverter converter = TypeDescriptor.GetConverter(Type);
						return converter.ConvertTo(obj, Type);
					}
					switch (type)
					{
					case BindingValueType.Boolean:
						return boolValue;
					case BindingValueType.Bounds:
						return boundsValue;
					case BindingValueType.Color:
						return colorValue;
					case BindingValueType.Float:
						return floatValue;
					case BindingValueType.Integer:
						return intValue;
					case BindingValueType.Rect:
						return rectValue;
					case BindingValueType.Reference:
						return referenceValue;
					case BindingValueType.String:
						return stringValue;
					case BindingValueType.Vector2:
						return vector2Value;
					case BindingValueType.Vector3:
						return vector3Value;
					case BindingValueType.Vector4:
						return vector4Value;
					default:
						return null;
					}
				}
				set
				{
					if (value != null || type == BindingValueType.Reference)
					{
						switch (type)
						{
						case BindingValueType.Boolean:
							boolValue = (bool)value;
							break;
						case BindingValueType.Bounds:
							boundsValue = (Bounds)value;
							break;
						case BindingValueType.Color:
							colorValue = (Color)value;
							break;
						case BindingValueType.Float:
							floatValue = (float)value;
							break;
						case BindingValueType.Integer:
							intValue = (int)value;
							break;
						case BindingValueType.Rect:
							rectValue = (Rect)value;
							break;
						case BindingValueType.Reference:
							referenceValue = (UnityEngine.Object)value;
							break;
						case BindingValueType.String:
							stringValue = (string)value;
							break;
						case BindingValueType.Vector2:
							vector2Value = (Vector2)value;
							break;
						case BindingValueType.Vector3:
							vector3Value = (Vector3)value;
							break;
						case BindingValueType.Vector4:
							vector4Value = (Vector4)value;
							break;
						}
					}
				}
			}

			[Inspect("IsExternal")]
			public GameObject GameObject
			{
				get
				{
					return gameObject;
				}
				set
				{
					if (gameObject != value)
					{
						gameObject = value;
						Component = null;
					}
				}
			}

			[Inspect("IsExternal")]
			[Restrict("GetComponents")]
			public UnityEngine.Component Component
			{
				get
				{
					return component;
				}
				set
				{
					if (component != value)
					{
						component = value;
						Method = null;
					}
				}
			}

			[Inspect("IsExternal")]
			[Restrict("GetMethods", RestrictDisplay.Toolbox)]
			public MethodInfo Method
			{
				get
				{
					return GetMethodInfo();
				}
				set
				{
					if (value == null)
					{
						method = string.Empty;
						return;
					}
					MethodInfo methodInfo = GetMethodInfo();
					if (methodInfo != value)
					{
						method = value.Name;
					}
				}
			}

			public BindingParameter()
			{
			}

			public BindingParameter(bool canBeInternal)
			{
				this.canBeInternal = canBeInternal;
			}

			private IList RestrictBinding()
			{
				List<object> list = new List<object>();
				if (canBeInternal)
				{
					list.Add(BindingType.Internal);
				}
				list.Add(BindingType.Static);
				list.Add(BindingType.External);
				return list;
			}

			private IList GetComponents()
			{
				List<DescriptorPair> list = new List<DescriptorPair>();
				if (gameObject == null)
				{
					return list;
				}
				UnityEngine.Component[] components = gameObject.GetComponents(typeof(UnityEngine.Component));
				foreach (UnityEngine.Component component in components)
				{
					list.Add(new DescriptorPair(component, new DescriptorAttribute(component.GetType().Name, string.Empty)));
				}
				return list;
			}

			private IList GetMethods()
			{
				List<DescriptorPair> list = new List<DescriptorPair>();
				if (gameObject == null || component == null)
				{
					return list;
				}
				MethodInfo[] methods = component.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
				foreach (MethodInfo methodInfo in methods)
				{
					if (!methodInfo.IsGenericMethod && !methodInfo.IsConstructor && !methodInfo.IsFinal && !methodInfo.IsSpecialName && IsMethodValid(methodInfo))
					{
						string name = methodInfo.ReturnType.Name + " " + methodInfo.Name + "()";
						list.Add(new DescriptorPair(methodInfo, new DescriptorAttribute(name, string.Empty)));
					}
				}
				PropertyInfo[] properties = component.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
				foreach (PropertyInfo propertyInfo in properties)
				{
					if (propertyInfo.CanRead)
					{
						MethodInfo getMethod = propertyInfo.GetGetMethod();
						if (getMethod != null && IsMethodValid(getMethod))
						{
							string name2 = getMethod.ReturnType.Name + " " + propertyInfo.Name + "()";
							list.Add(new DescriptorPair(getMethod, new DescriptorAttribute(name2, string.Empty)));
						}
					}
				}
				return list;
			}

			private bool IsMethodValid(MethodInfo info)
			{
				ParameterInfo[] parameters = info.GetParameters();
				if (parameters.Length > 0)
				{
					return false;
				}
				if (info.ReturnType == null || info.ReturnType == typeof(void))
				{
					return false;
				}
				TypeConverter converter = TypeDescriptor.GetConverter(info.ReturnType);
				if (!Type.IsAssignableFrom(info.ReturnType) && !converter.CanConvertTo(Type))
				{
					return false;
				}
				return true;
			}

			private MethodInfo GetMethodInfo()
			{
				if (gameObject == null || component == null || string.IsNullOrEmpty(method))
				{
					return null;
				}
				return component.GetType().GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			}

			private bool IsStatic()
			{
				return binding == BindingType.Static;
			}

			private bool IsExternal()
			{
				return binding == BindingType.External;
			}

			private Type GetRuntimeType()
			{
				return Type;
			}

			public static bool IsValidType(Type type)
			{
				if (type == typeof(bool))
				{
					return true;
				}
				if (type == typeof(Bounds))
				{
					return true;
				}
				if (type == typeof(Color))
				{
					return true;
				}
				if (type == typeof(float))
				{
					return true;
				}
				if (type == typeof(int))
				{
					return true;
				}
				if (type == typeof(Rect))
				{
					return true;
				}
				if (typeof(UnityEngine.Object).IsAssignableFrom(type))
				{
					return true;
				}
				if (type == typeof(string))
				{
					return true;
				}
				if (type == typeof(Vector2))
				{
					return true;
				}
				if (type == typeof(Vector3))
				{
					return true;
				}
				if (type == typeof(Vector4))
				{
					return true;
				}
				return false;
			}

			private object Invoke()
			{
				if (gameObject == null || component == null || string.IsNullOrEmpty(method))
				{
					return null;
				}
				MethodInfo methodInfo = GetMethodInfo();
				if (methodInfo == null)
				{
					return null;
				}
				return methodInfo.Invoke(component, new object[0]);
			}

			public bool Copiable(object destination)
			{
				BindingParameter bindingParameter = destination as BindingParameter;
				if (bindingParameter == null)
				{
					return false;
				}
				if (bindingParameter.type != type)
				{
					return false;
				}
				if (!bindingParameter.canBeInternal && binding == BindingType.Internal)
				{
					return false;
				}
				return true;
			}

			public object Copy(object destination)
			{
				BindingParameter bindingParameter = destination as BindingParameter;
				BindingParameter bindingParameter2 = new BindingParameter();
				if (bindingParameter != null)
				{
					bindingParameter2.canBeInternal = bindingParameter.canBeInternal;
				}
				else
				{
					bindingParameter2.canBeInternal = canBeInternal;
				}
				bindingParameter2.binding = binding;
				bindingParameter2.boolValue = boolValue;
				bindingParameter2.boundsValue = boundsValue;
				bindingParameter2.colorValue = colorValue;
				bindingParameter2.component = component;
				bindingParameter2.floatValue = floatValue;
				bindingParameter2.gameObject = gameObject;
				bindingParameter2.intValue = intValue;
				bindingParameter2.method = method;
				bindingParameter2.qualifiedTypeName = qualifiedTypeName;
				bindingParameter2.rectValue = rectValue;
				bindingParameter2.referenceValue = referenceValue;
				bindingParameter2.stringValue = stringValue;
				bindingParameter2.type = type;
				bindingParameter2.vector2Value = vector2Value;
				bindingParameter2.vector3Value = vector3Value;
				bindingParameter2.vector4Value = vector4Value;
				return bindingParameter2;
			}

			public override string ToString()
			{
				return type.ToString();
			}
		}

		[SerializeField]
		private string[] internalParameters = new string[0];

		[SerializeField]
		private GameObject gameObject;

		[SerializeField]
		private UnityEngine.Component component;

		[SerializeField]
		private string method;

		[Inspect]
		[SerializeField]
		[Collection(0, false)]
		private BindingParameter[] parameters = new BindingParameter[0];

		[Inspect]
		public GameObject GameObject
		{
			get
			{
				return gameObject;
			}
			set
			{
				if (gameObject != value)
				{
					gameObject = value;
					Component = null;
				}
			}
		}

		[Restrict("GetComponents")]
		[Inspect]
		public UnityEngine.Component Component
		{
			get
			{
				return component;
			}
			set
			{
				if (component != value)
				{
					component = value;
					Method = null;
				}
			}
		}

		[Inspect]
		[Restrict("GetMethods", RestrictDisplay.Toolbox)]
		public MethodInfo Method
		{
			get
			{
				return GetMethodInfo();
			}
			set
			{
				if (value == null)
				{
					parameters = new BindingParameter[0];
					method = string.Empty;
					return;
				}
				MethodInfo methodInfo = GetMethodInfo();
				if (methodInfo == value)
				{
					return;
				}
				method = value.Name;
				ParameterInfo[] array = value.GetParameters();
				parameters = new BindingParameter[array.Length];
				for (int i = 0; i < array.Length; i++)
				{
					parameters[i] = new BindingParameter(internalParameters.Length > i);
					parameters[i].Type = array[i].ParameterType;
					if (internalParameters.Length > i)
					{
						parameters[i].binding = BindingParameter.BindingType.Internal;
					}
				}
			}
		}

		public event ActionEventHandler OnInvoke;

		public ActionBinding()
		{
		}

		public ActionBinding(Type[] types)
		{
			internalParameters = new string[types.Length];
			for (int i = 0; i < types.Length; i++)
			{
				internalParameters[i] = types[i].AssemblyQualifiedName;
			}
		}

		private IList GetComponents()
		{
			List<DescriptorPair> list = new List<DescriptorPair>();
			if (gameObject == null)
			{
				return list;
			}
			UnityEngine.Component[] components = gameObject.GetComponents(typeof(UnityEngine.Component));
			foreach (UnityEngine.Component component in components)
			{
				list.Add(new DescriptorPair(component, new DescriptorAttribute(component.GetType().Name, string.Empty)));
			}
			return list;
		}

		private IList GetMethods()
		{
			List<DescriptorPair> list = new List<DescriptorPair>();
			if (gameObject == null || component == null)
			{
				return list;
			}
			MethodInfo[] methods = component.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			foreach (MethodInfo methodInfo in methods)
			{
				if (!methodInfo.IsGenericMethod && !methodInfo.IsConstructor && !methodInfo.IsFinal && !methodInfo.IsSpecialName && methodInfo.DeclaringType != typeof(object) && methodInfo.DeclaringType != typeof(UnityEngine.Component) && IsMethodValid(methodInfo))
				{
					ParameterInfo[] param = methodInfo.GetParameters();
					list.Add(new DescriptorPair(methodInfo, new DescriptorAttribute(GetParamNames(methodInfo.Name, param), string.Empty)));
				}
			}
			PropertyInfo[] properties = component.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			foreach (PropertyInfo propertyInfo in properties)
			{
				if (propertyInfo.CanWrite && propertyInfo.DeclaringType != typeof(object) && propertyInfo.DeclaringType != typeof(UnityEngine.Object) && propertyInfo.DeclaringType != typeof(UnityEngine.Component))
				{
					MethodInfo setMethod = propertyInfo.GetSetMethod();
					if (setMethod != null && IsMethodValid(setMethod))
					{
						ParameterInfo[] param2 = setMethod.GetParameters();
						list.Add(new DescriptorPair(setMethod, new DescriptorAttribute(GetParamNames(propertyInfo.Name, param2), string.Empty)));
					}
				}
			}
			return list;
		}

		private bool IsMethodValid(MethodInfo info)
		{
			ParameterInfo[] array = info.GetParameters();
			bool result = true;
			for (int i = 0; i < array.Length; i++)
			{
				if (!BindingParameter.IsValidType(array[i].ParameterType))
				{
					result = false;
					break;
				}
				if (internalParameters.Length > i)
				{
					Type type = Type.GetType(internalParameters[i]);
					if (!type.IsAssignableFrom(array[i].ParameterType))
					{
						result = false;
						break;
					}
				}
			}
			return result;
		}

		private string GetParamNames(string name, ParameterInfo[] param)
		{
			string text = name + " (";
			for (int i = 0; i < param.Length; i++)
			{
				text += param[i].ParameterType.Name;
				if (i < param.Length - 1)
				{
					text += ", ";
				}
			}
			return text + ")";
		}

		private MethodInfo GetMethodInfo()
		{
			if (gameObject == null || component == null || string.IsNullOrEmpty(method))
			{
				return null;
			}
			Type[] array = new Type[parameters.Length];
			for (int i = 0; i < parameters.Length; i++)
			{
				array[i] = parameters[i].Type;
			}
			return component.GetType().GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, array, null);
		}

		public void Invoke(params object[] args)
		{
			if (gameObject == null || component == null || string.IsNullOrEmpty(method))
			{
				return;
			}
			MethodInfo methodInfo = GetMethodInfo();
			if (methodInfo == null)
			{
				return;
			}
			object[] array = new object[parameters.Length];
			for (int i = 0; i < parameters.Length; i++)
			{
				if (args.Length > i && parameters[i].binding == BindingParameter.BindingType.Internal)
				{
					array[i] = args[i];
				}
				else
				{
					array[i] = parameters[i].Value;
				}
			}
			methodInfo.Invoke(component, array);
			if (this.OnInvoke != null)
			{
				this.OnInvoke(this, array);
			}
		}

		public override string ToString()
		{
			string text = "(";
			for (int i = 0; i < internalParameters.Length; i++)
			{
				text += Type.GetType(internalParameters[i]).Name;
				if (i < internalParameters.Length - 1)
				{
					text += ", ";
				}
			}
			text += ")";
			if (component != null)
			{
				return component.GetType().Name + " : " + method + " : " + text;
			}
			return text;
		}

		public bool Copiable(object destination)
		{
			ActionBinding actionBinding = destination as ActionBinding;
			if (actionBinding == null)
			{
				return false;
			}
			if (actionBinding.internalParameters.Length != internalParameters.Length)
			{
				return false;
			}
			for (int i = 0; i < internalParameters.Length; i++)
			{
				if (internalParameters[i] != actionBinding.internalParameters[i])
				{
					return false;
				}
			}
			return true;
		}
	}
}
